package com.jzo2o.health.service.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.api.trade.NativePayApi;
import com.jzo2o.api.trade.TradingApi;
import com.jzo2o.api.trade.dto.request.NativePayReqDTO;
import com.jzo2o.api.trade.dto.response.NativePayResDTO;
import com.jzo2o.api.trade.dto.response.TradingResDTO;
import com.jzo2o.api.trade.enums.PayChannelEnum;
import com.jzo2o.api.trade.enums.TradingStateEnum;
import com.jzo2o.common.expcetions.BadRequestException;
import com.jzo2o.common.expcetions.DBException;
import com.jzo2o.common.expcetions.ForbiddenOperationException;
import com.jzo2o.common.model.PageResult;
import com.jzo2o.common.utils.BeanUtils;
import com.jzo2o.common.utils.DateUtils;
import com.jzo2o.common.utils.ObjectUtils;
import com.jzo2o.health.enums.OrderPayStatusEnum;
import com.jzo2o.health.enums.OrderStatusEnum;
import com.jzo2o.health.mapper.MemberMapper;
import com.jzo2o.health.mapper.OrdersMapper;
import com.jzo2o.health.mapper.ReservationSettingMapper;
import com.jzo2o.health.mapper.SetmealMapper;
import com.jzo2o.health.model.UserThreadLocal;
import com.jzo2o.health.model.domain.*;
import com.jzo2o.health.model.dto.OrderCountDTO;
import com.jzo2o.health.model.dto.request.OrdersPageQueryReqDTO;
import com.jzo2o.health.model.dto.request.PlaceOrderReqDTO;
import com.jzo2o.health.model.dto.response.*;
import com.jzo2o.health.properties.TradeProperties;
import com.jzo2o.health.service.IOrdersCancelledService;
import com.jzo2o.health.service.IOrdersService;
import com.jzo2o.mysql.utils.PageUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 订单表 服务实现类
 * </p>
 *
 * @author author
 * @since 2024-12-14
 */
@Service
public class OrdersServiceImpl extends ServiceImpl<OrdersMapper, Orders> implements IOrdersService {
    @Resource
    private RedisTemplate redisTemplate;
    @Resource
    private SetmealMapper setmealMapper;
    @Resource
    private MemberMapper memberMapper;
    @Resource
    private ReservationSettingMapper reservationSettingMapper;

    /**
     * 用户端预约下单
     *
     * @param placeOrderReqDTO
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public PlaceOrderResDTO placeOrder(PlaceOrderReqDTO placeOrderReqDTO) {
        ReservationSetting reservationSetting = reservationSettingMapper.selectOne(new LambdaQueryWrapper<ReservationSetting>()
                .eq(ReservationSetting::getOrderDate, placeOrderReqDTO.getReservationDate()));
        if (reservationSetting == null || reservationSetting.getReservations() >= reservationSetting.getNumber()) {
            throw new ForbiddenOperationException("预约已满");
        }
        Long orderId = generateOrderId();
        Orders orders = new Orders();
        //订单基础信息
        orders.setSetmealId(placeOrderReqDTO.getSetmealId());//套餐id
        orders.setCheckupPersonPhone(placeOrderReqDTO.getCheckupPersonPhone());//体检人电话
        orders.setCheckupPersonName(placeOrderReqDTO.getCheckupPersonName());//体检人姓名
        orders.setCheckupPersonSex(placeOrderReqDTO.getCheckupPersonSex());//体检人性别
        orders.setCheckupPersonIdcard(placeOrderReqDTO.getCheckupPersonIdcard());//体检人身份证号
        orders.setReservationDate(placeOrderReqDTO.getReservationDate());//预约日期
        orders.setOrderStatus(0);//订单状态
        orders.setPayStatus(0);
        orders.setId(orderId);//订单id
        orders.setSortBy(DateUtils.toEpochMilli(LocalDateTime.now()) + orders.getId() % 10000); //根据服务开始时间转为毫秒时间戳+订单后5位
        //用户信息
        Member member = memberMapper.selectById(UserThreadLocal.currentUserId());
        orders.setMemberId(member.getId());//用户id
        orders.setMemberPhone(member.getPhone());//用户手机号
        //套餐信息
        Setmeal setmeal = setmealMapper.selectById(placeOrderReqDTO.getSetmealId());
        orders.setSetmealName(setmeal.getName());//套餐名称
        orders.setSetmealPrice(BigDecimal.valueOf(setmeal.getPrice()));//套餐价格
        orders.setSetmealImg(setmeal.getImg());//套餐图片
        orders.setSetmealAge(setmeal.getAge());//套餐适用年龄
        orders.setSetmealSex(setmeal.getSex());//套餐适用性别
        orders.setSetmealRemark(setmeal.getRemark());//套餐备注
        int count = baseMapper.insert(orders);
        if (count <= 0) {
            throw new DBException("下单失败");
        }
        Integer i = reservationSettingMapper.updateReservations(reservationSetting.getId());
        if (i <= 0) {
            throw new DBException("更新预约人数失败");
        }
        return new PlaceOrderResDTO(orderId);
    }

    public static final String INCREMENT_KEY = "order_id";

    public Long generateOrderId() {
        Long idInit = DateUtils.getFormatDate(LocalDateTime.now(), "yyMMdd") * 10000000000000L;
        Long increment = redisTemplate.opsForValue().increment(INCREMENT_KEY);
        return idInit + increment;
    }


    @Resource
    private NativePayApi nativePayApi;
    @Resource
    private TradeProperties tradeProperties;
    @Resource
    private TradingApi tradingApi;
    @Resource
    private IOrdersCancelledService ordersCancelledService;

    /**
     * 用户端订单支付
     *
     * @param id
     * @param tradingChannel
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public OrdersPayResDTO pay(Long id, PayChannelEnum tradingChannel) {
        Orders orders = this.getById(id);
        //判断订单是否存在
        if (orders == null) {
            throw new ForbiddenOperationException("订单不存在");
        }
        //2.判断订单是否处理过（已支付且有交易订单号）
        if (orders.getPayStatus().equals(OrderPayStatusEnum.PAY_SUCCESS.getStatus()) ||
                ObjectUtils.isNotNull(orders.getTradingOrderNo())) {
            OrdersPayResDTO ordersPayResDTO = BeanUtils.copyBean(orders, OrdersPayResDTO.class);
            ordersPayResDTO.setProductOrderNo(id);
            return ordersPayResDTO;
        }
        //3.调用支付服务的支付接口
        //3.1 根据支付渠道查询商户号
        Long enterpriseId = ObjectUtils.equals(tradingChannel, PayChannelEnum.ALI_PAY)
                ? tradeProperties.getAliEnterpriseId() : tradeProperties.getWechatEnterpriseId();

        //3.2 判断是否更换过支付渠道
        boolean changeChannel = false; //默认未换过
        if (ObjectUtils.isNotNull(orders.getTradingChannel()) && !orders.getTradingChannel()
                .equals(tradingChannel)) {
            changeChannel = true;
        }

        //3.3 调用支付的Feign接口
        NativePayReqDTO nativePayDto = new NativePayReqDTO();
        nativePayDto.setProductOrderNo(id);//业务系统订单号
        nativePayDto.setChangeChannel(changeChannel);//是否更换过支付渠道
        nativePayDto.setTradingChannel(tradingChannel);//支付渠道
        nativePayDto.setEnterpriseId(enterpriseId);//商户号
        //nativePayDto.setTradingAmount(orders.getRealPayAmount());
        nativePayDto.setTradingAmount(new BigDecimal(0.01));//一定要设置为0.01.这样方便测试
        nativePayDto.setMemo(orders.getSetmealName());//商品描述
        nativePayDto.setProductAppId(tradeProperties.getProductAppId());//业务系统标识
        NativePayResDTO nativePayResDTO = nativePayApi.createDownLineTrading(nativePayDto);
        //3.4 更新订单表数据
        if (ObjectUtils.isNull(nativePayResDTO)) {
            throw new BadRequestException("调用支付接口失败");
        }


        boolean result = this.lambdaUpdate()
                .eq(Orders::getId, id)
                .set(Orders::getTradingChannel, nativePayResDTO.getTradingChannel()) //支付渠道
                .set(Orders::getTradingOrderNo, nativePayResDTO.getTradingOrderNo()) //交易订单号
                .update();
        if (!result) {
            throw new DBException("更新订单失败");
        }

        //4.处理响应数据
        OrdersPayResDTO ordersPayResDTO = BeanUtils.copyBean(nativePayResDTO, OrdersPayResDTO.class);
        ordersPayResDTO.setPayStatus(orders.getPayStatus());
        return ordersPayResDTO;
    }

    /**
     * 用户端查询支付结果
     *
     * @param id
     * @return
     */
    @Override
    public OrdersPayResDTO queryPayResult(Long id) {
        //1.判断订单是否存在
        Orders orders = this.getById(id);
        if (orders == null) {
            throw new BadRequestException("订单不存在");
        }

        //2.判断是否有交易单号
        if (ObjectUtils.isNull(orders.getTradingOrderNo())) {
            throw new BadRequestException("非法订单");
        }

        OrdersPayResDTO ordersPayResDTO = BeanUtils.copyBean(orders, OrdersPayResDTO.class);
        ordersPayResDTO.setProductOrderNo(id);
        if (ObjectUtils.equals(orders.getPayStatus(), OrderPayStatusEnum.PAY_SUCCESS.getStatus())) {
            return ordersPayResDTO;
        }

        //4.调用支付服务Feign接口查询支付结果
        TradingResDTO tradingResDTO = tradingApi.findTradResultByTradingOrderNo(orders.getTradingOrderNo());

        //5.更新订单表信息（支付时间、第三方交易号、订单状态、支付状态）
        //update orders set pay_time=?,transaction_id=?,order_status=?,pay_status=? where id=?
        if (ObjectUtils.isNotNull(tradingResDTO) && ObjectUtils.equals(tradingResDTO.getTradingState(), TradingStateEnum.YJS)) {
            this.lambdaUpdate().set(Orders::getPayTime, tradingResDTO.getPaySuccessTime())
                    .set(Orders::getTransactionId, tradingResDTO.getTransactionId())
                    .set(Orders::getOrderStatus, OrderStatusEnum.WAITING_CHECKUP.getStatus())
                    .set(Orders::getPayStatus, OrderPayStatusEnum.PAY_SUCCESS.getStatus())
                    .eq(Orders::getId, id)
                    .update();

            //6.封装响应数据
            ordersPayResDTO.setQrCode(tradingResDTO.getQrCode());//支付二维码图片地址
            ordersPayResDTO.setPayStatus(OrderPayStatusEnum.PAY_SUCCESS.getStatus());//支付成功
        }
        return ordersPayResDTO;
    }


    /**
     * 用户端订单列表查询(条件分页查询)
     *
     * @param ordersStatus
     * @param sortBy
     * @return
     */
    @Override
    public List<OrdersResDTO> pageQuery(Integer ordersStatus, Long sortBy) {
        LambdaQueryWrapper<Orders> wrapper = new LambdaQueryWrapper<Orders>()
                .eq(ObjectUtils.isNotNull(ordersStatus), Orders::getOrderStatus, ordersStatus)
                .lt(ObjectUtils.isNotNull(sortBy), Orders::getSortBy, sortBy)
                .eq(Orders::getMemberId, UserThreadLocal.currentUserId());
        Page<Orders> ordersPage = new Page<>();
        ordersPage.addOrder(OrderItem.desc("sort_by"));
        ordersPage.setSearchCount(false);

        Page<Orders> page = baseMapper.selectPage(ordersPage, wrapper);
        List<Orders> records = page.getRecords();
        return BeanUtils.copyList(records, OrdersResDTO.class);
    }

    /**
     * 用户端根据ID查询订单详情
     *
     * @param id
     * @return
     */
    @Override
    public OrdersDetailResDTO detail(Long id) {
        Orders orders = this.getById(id);
        OrdersCancelled ordersCancelled = ordersCancelledService.getById(id);

        if (ObjectUtils.isNotNull(orders)) {
            OrdersDetailResDTO ordersDetailResDTO = BeanUtils.copyBean(orders, OrdersDetailResDTO.class);
            if (ObjectUtils.isNotNull(ordersCancelled)) {
                ordersDetailResDTO.setCancelReason(ordersCancelled.getCancelReason());
                ordersDetailResDTO.setCancelTime(ordersCancelled.getCancelTime());
            }
            ordersDetailResDTO.setMemberId(UserThreadLocal.currentUserId());
            return ordersDetailResDTO;
        } else {
            throw new BadRequestException("订单不存在");
        }
    }


    /**
     * 管理端订单列表查询(条件分页查询)
     *
     * @param ordersPageQueryReqDTO
     * @return
     */
    @Override
    public PageResult<OrdersResDTO> pageQuery(OrdersPageQueryReqDTO ordersPageQueryReqDTO) {
        Page<Orders> page = PageUtils.parsePageQuery(ordersPageQueryReqDTO, Orders.class);


        LambdaQueryWrapper<Orders> wrapper = new LambdaQueryWrapper<Orders>()
                .eq(ObjectUtils.isNotNull(ordersPageQueryReqDTO.getOrderStatus()), Orders::getOrderStatus, ordersPageQueryReqDTO.getOrderStatus())
                .like(StrUtil.isNotBlank(ordersPageQueryReqDTO.getMemberPhone()), Orders::getMemberPhone, ordersPageQueryReqDTO.getMemberPhone());
        Page<Orders> ordersPage = baseMapper.selectPage(page, wrapper);
        return PageUtils.toPage(ordersPage, OrdersResDTO.class);
    }

    /**
     * 管理端订单详情查询
     *
     * @param id
     * @return
     */
    @Override
    public AdminOrdersDetailResDTO aggregation(Long id) {
        Orders orders = this.getById(id);
        OrdersCancelled ordersCancelled = ordersCancelledService.getById(id);
        AdminOrdersDetailResDTO adminOrdersDetailResDTO = new AdminOrdersDetailResDTO();

        if (ObjectUtils.isNotNull(orders)) {
            adminOrdersDetailResDTO.setOrderInfo(BeanUtils.copyBean(orders, AdminOrdersDetailResDTO.OrderInfo.class));
            if (ObjectUtils.isNotNull(ordersCancelled)) {
                adminOrdersDetailResDTO.setCancelInfo(AdminOrdersDetailResDTO.CancelInfo.builder()
                        .cancelReason(ordersCancelled.getCancelReason())
                        .cancelTime(ordersCancelled.getCancelTime()).build());

                if (orders.getPayStatus()==0) {
                    adminOrdersDetailResDTO.setPayInfo(AdminOrdersDetailResDTO.PayInfo.builder()
                            .payStatus(orders.getPayStatus()).build());
                }else {
                    adminOrdersDetailResDTO.setPayInfo(AdminOrdersDetailResDTO.PayInfo.builder()
                            .payStatus(1)
                            .payTime(orders.getPayTime())
                            .thirdOrderId(orders.getTransactionId())
                            .tradingChannel(orders.getTradingChannel()).build());
                }
                if (orders.getPayStatus()>1) {
                    adminOrdersDetailResDTO.setRefundInfo(AdminOrdersDetailResDTO.RefundInfo.builder()
                            .cancelReason(ordersCancelled.getCancelReason())
                            .cancelTime(ordersCancelled.getCancelTime())
                            .refundId(orders.getRefundId())
                            .refundStatus(orders.getPayStatus())
                            .tradingChannel(orders.getTradingChannel()).build());
                }


            }
            return adminOrdersDetailResDTO;
        } else {
            throw new BadRequestException("订单不存在");
        }
    }


    /**
     * 统计订单状态数量
     *
     * @return
     */
    @Override
    public OrdersCountResDTO countByStatus() {
        List<OrderCountDTO> orderCountDTOS = baseMapper.countByStatus();
        OrdersCountResDTO ordersCountResDTO = new OrdersCountResDTO();
        Map<String, Integer> stringIntegerMap = new HashMap<>();
        stringIntegerMap.put("zhengxin", 0);
        orderCountDTOS.forEach(item -> {
            switch (item.getOrderStatus()) {
                case 0: ordersCountResDTO.setNoPayCount(item.getCount()); break;
                case 100: ordersCountResDTO.setWaitingCheckupCount(item.getCount()); break;
                case 200: ordersCountResDTO.setCompletedCheckupCount(item.getCount()); break;
                case 300: ordersCountResDTO.setClosedCount(item.getCount()); break;
                case 400: ordersCountResDTO.setCancelledCount(item.getCount()); break;
            }
            stringIntegerMap.put("zhengxin", stringIntegerMap.get("zhengxin") + item.getCount());
        });
        ordersCountResDTO.setTotalCount(stringIntegerMap.get("zhengxin"));
        return ordersCountResDTO;
    }
}
